/*
 * Copyright 2007 - The JDPF Project Team (http://www.jdpf.org) 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */
package org.jdpf.core.kernel.execution;

import java.util.ArrayList;
import java.util.List;

import org.jdpf.core.kernel.elements.Net;
import org.jdpf.core.kernel.elements.Place;
import org.jdpf.core.kernel.elements.Token;
import org.jdpf.core.kernel.exceptions.CoreException;
import org.jdpf.core.kernel.logging.AbstractLoggingService;
import org.jdpf.core.kernel.logging.ILoggable;
import org.jdpf.core.plugins.datatypes.AbstractDataType;
import org.jdpf.core.plugins.parameters.Parameters;

public class Task extends AbstractLoggingService implements Runnable, ILoggable{

	private String netFile, paramFile, netName, taskName;
	private boolean isValid, isAborted;
	private Parameters param;
	private NetExecutor net;
	
	private Thread t;
	
	/*
	 * Priority not used yet.
	 */	
	public Task(String netFile, String paramFile, int priority) {
		this.netFile = netFile;
		this.paramFile = paramFile;
		this.taskName = paramFile;
		this.taskLogging = false;
		isAborted=false;
	}

	public Task(String netFile, String paramFile) {
		this(netFile, paramFile, 0);
	}

	public Task() { 
		this(null, null, 0);
	}

	public String getTaskId() {
		return this.paramFile;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Runnable#run() This method run the net
	 */
	public void run() {
		while (t == Thread.currentThread()) {
			try {
			
				if(taskLogging) 
					logger.logMainPoint(this, this.getTaskId(), "Task started!");
				
				net.process();
				
				if(!net.isPaused() && taskLogging) 
					logger.logMainPoint(this, this.getTaskId(), isAborted ? "Task aborted!" : "Task completed!");
				t = null;
			} catch (Exception e) {
				// Fired when illegal parameter is detected
				logger.error(this, this.getTaskId(), "Task exception " + (e!=null ? ": " + e.getMessage(): ""));
				// For stopping the process
				break;
			}
		}
	}

	/**
	 * Method to set the task ready to be run by the engine.
	 * 
	 * @throws CoreException
	 */
	public void start() throws CoreException {
		net.setPause(false);
		net.start();

		Token in = new Token();
		List<AbstractDataType> inList = new ArrayList<AbstractDataType>(2);
		inList.add(param);
		in.setValueList(inList);

		try {
			// Puts the token in every start place (there could be more than one)
			// This is the token with the parametrization
			for (Place p : net.getStartPlaces()) {
				p.addToken(in);
			}			
			
			// Parametrize
			net.process();
			
			// An empty token is placed in all the start places to anable them
			for (Place p : net.getStartPlaces()) {
				p.addToken(new Token());
			}

			// Starting the thread
			t = new Thread(this);
			t.start();
		} catch (Exception e) {
			if(e.getMessage() != null) {
				logger.error(this, e.getMessage()); 
			} else {
				if(e != null) {
					if(e.getCause()!=null)
						if(e.getCause().getMessage()!=null)
							logger.error(this, e.getCause().getMessage()); 
						else 
							logger.error(this, e.getCause().toString());
					else 
						logger.error(this, e.toString());
				} else {
					logger.error(this, "Unrecognized exception!");
					e.printStackTrace();
				}
			}
			// Task abortion
			this.abort();
		}
	}

	public void pause() {
		if(taskLogging) 
			logger.log(this, net.getNetName(), net.isPaused() ? "RESTARTING":"PAUSING");

		// Toggle pause status
		net.setPause(!net.isPaused());
		
		if (!net.isPaused()) {
			try {
				t = new Thread(this);
				t.start();
			} catch (Exception e) {
				if(e.getMessage() != null) 
					logger.error(this, e.getMessage()); 
				else
					logger.error(this, e.getCause().getMessage()); 
			}
		}
	}
	
	public boolean isPaused() {
		return net.isPaused();
	}
	
	/**
	 * This method has been defined to distinguish between a regular
	 * task stop and a task abortion due to exceptions.
	 */
	public void abort() {
		isAborted = true; // This flag is to be here for concurrency reasons
		stop();
	}
	
	public boolean isRunning(){
		if(t!=null)
			return t.isAlive();
		return false;
	}

	public void stop() {
		if(taskLogging) logger.log(this, net.getNetName(), "STOPPING");
		net.setPause(false);
		net.stop();
		t = null;
		if(taskLogging) logger.log(this, net.getNetName(), "STOPPED");
	}


	public void checkParameters() throws CoreException {

		net.checkParameters(param);
	}

	public String toString() {
		return "Task{Net file=" + netFile + "; Parameter file=" + paramFile
				+ "}";
	}

	/*
	 * Logging
	 */
	public void setTaskLogging(String taskLogging) {
		if(Boolean.parseBoolean(taskLogging)) logger.log(this, this.getTaskName(), "Enable logging");
		this.taskLogging = Boolean.parseBoolean(taskLogging);
	}
	
	/*
	 * Getters and setters
	 */
	public String getTaskName() {
		return taskName;
	}

	public void setTaskName(String taskName) {
		this.taskName = taskName;
	}
	
	public String getNetName() {
		return netName;
	}

	public void setNetName(String netName) {
		this.netName = netName;
	}
	
	public Net getNet() {
		return net.getNet();
	}

	public void setNet(Net net) {
		this.net = new NetExecutor(this, net);
	}

	public Parameters getParam() {
		return param;
	}

	public void setParam(Parameters param) {
		this.param = param;
	}
	
	public String getNetFile() {
		return netFile;
	}

	public void setNetFile(String netFile) {
		this.netFile = netFile;
	}

	public String getParamFile() {
		return paramFile;
	}

	public void setParamFile(String paramFile) {
		this.paramFile = paramFile;
	}
	
	public boolean isValid() {
		return isValid;
	}

	public void setValidity(boolean isValid) {
		this.isValid = isValid;
	}

	public boolean isAborted() {
		return isAborted;
	}
}
